package com.gitee.feizns.dynamic.convert;

import com.gitee.feizns.dynamic.Ex;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * <b>解析日期类型工具类</b>
 * @author feizns
 * @since 2022/11/19
 */
public abstract class ParseToDateTypeUtils {

    /**
     * 日期解析器
     */
    private static final ThreadLocal<SimpleDateFormat> LOCAL_FORMAT = ThreadLocal.withInitial(SimpleDateFormat::new);

    /**
     * 日期长度和日期格式的映射
     */
    private static final DatePattern DATE_PATTERN = new DatePattern();

    static {
        DATE_PATTERN.register("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
        DATE_PATTERN.register("yyyy-MM-dd'T'HH:mm:ssZ");
        DATE_PATTERN.register("yyyy-MM-dd'T'HH:mmZ");
        DATE_PATTERN.register("yyyy-MM-dd'T'HHZ");

        DATE_PATTERN.register("yyyy-MM-dd' 'HH:mm:ss.SSSZ");
        DATE_PATTERN.register("yyyy-MM-dd' 'HH:mm:ssZ");
        DATE_PATTERN.register("yyyy-MM-dd' 'HH:mmZ");
        DATE_PATTERN.register("yyyy-MM-dd' 'HHZ");

        DATE_PATTERN.register("yyyy-MM-dd'T'HH:mm:ss.SSS");
        DATE_PATTERN.register("yyyy-MM-dd'T'HH:mm:ss");
        DATE_PATTERN.register("yyyy-MM-dd'T'HH:mm");
        DATE_PATTERN.register("yyyy-MM-dd'T'HH");

        DATE_PATTERN.register("yyyy-MM-dd HH:mm:ss.SSS");
        DATE_PATTERN.register("yyyy-MM-dd HH:mm:ss");
        DATE_PATTERN.register("yyyy-MM-dd HH:mm");
        DATE_PATTERN.register("yyyy-MM-dd HH");

        DATE_PATTERN.register("yyyy-MM-ddZ");
        DATE_PATTERN.register("yyyy-MM-dZ");
        DATE_PATTERN.register("yyyy-MMZ");
        DATE_PATTERN.register("yyyy-MZ");
        DATE_PATTERN.register("yyyyZ");

        DATE_PATTERN.register("HH:mm:ss.SSS");
        DATE_PATTERN.register("HH:mm:ss");
        DATE_PATTERN.register("HH:mm");
        DATE_PATTERN.register("HH");

        DATE_PATTERN.register("HH:mm:ss.SSSZ");
        DATE_PATTERN.register("HH:mm:ssZ");
        DATE_PATTERN.register("HH:mmZ");
        DATE_PATTERN.register("HHZ");

        DATE_PATTERN.register("yyyy-MM-dd");
        DATE_PATTERN.register("yyyy-MM-d");
        DATE_PATTERN.register("yyyy-M-d");
        DATE_PATTERN.register("yyyy-MM");
        DATE_PATTERN.register("yyyy-M");
        DATE_PATTERN.register("yyyy");
    }

    /**
     * <b>解析字符串为java.util.Date</b>
     * <pre>{@code
     *  SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
     * //UTC时间
     * System.out.println(sdf.format(to("2022-11-19T12:41:56.645Z"))); //2022-11-19 20:41:56.645
     * System.out.println(sdf.format(to("2022-11-19T12:41:56Z")));     //2022-11-19 20:41:56.000
     * System.out.println(sdf.format(to("2022-11-19T12:41Z")));        //2022-11-19 20:41:00.000
     * System.out.println(sdf.format(to("2022-11-19T12Z")));           //2022-11-19 20:00:00.000
     * System.out.println(sdf.format(to("2022-11-19 12:41:56.645Z"))); //2022-11-19 20:41:56.645
     * System.out.println(sdf.format(to("2022-11-19 12:41:56Z")));     //2022-11-19 20:41:56.000
     * System.out.println(sdf.format(to("2022-11-19 12:41Z")));        //2022-11-19 20:41:00.000
     * System.out.println(sdf.format(to("2022-11-19 12Z")));           //2022-11-19 20:00:00.000
     * System.out.println(sdf.format(to("2022-11-19Z")));              //2022-11-19 08:00:00.000
     * System.out.println(sdf.format(to("2022-11Z")));                 //2022-11-01 08:00:00.000
     * System.out.println(sdf.format(to("2022Z")));                    //2022-01-01 08:00:00.000
     * //本地时间
     * System.out.println(sdf.format(to("2022-11-19T12:41:56.645")));  //2022-11-19 12:41:56.645
     * System.out.println(sdf.format(to("2022-11-19T12:41:56")));      //2022-11-19 12:41:56.000
     * System.out.println(sdf.format(to("2022-11-19T12:41")));         //2022-11-19 12:41:00.000
     * System.out.println(sdf.format(to("2022-11-19T12")));            //2022-11-19 12:00:00.000
     * System.out.println(sdf.format(to("2022-11-19 12:41:56.645")));  //2022-11-19 12:41:56.645
     * System.out.println(sdf.format(to("2022-11-19 12:41:56")));      //2022-11-19 12:41:56.000
     * System.out.println(sdf.format(to("2022-11-19 12:41")));         //2022-11-19 12:41:00.000
     * System.out.println(sdf.format(to("2022-11-19 12")));            //2022-11-19 12:00:00.000
     * System.out.println(sdf.format(to("2022-11-19")));               //2022-11-19 00:00:00.000
     * System.out.println(sdf.format(to("2022-11")));                  //2022-11-01 00:00:00.000
     * System.out.println(sdf.format(to("2022")));                     //2022-01-01 00:00:00.000
     * }</pre>
     * @param datetime 日期字符串
     * @return 返回解析成功的日期，解析失败返回null
     */
    public static Date to(String datetime) {
        if ( datetime != null ) {
            //替换UTC时区
            int stringDateLength = datetime.length();
            if ( stringDateLength > 0 )
                datetime = replaceZtoUTC(datetime);
            Date date = parse(datetime, DATE_PATTERN.patterns(stringDateLength));
            if ( date != null )
                return date;
            //长度匹配失败的情况下遍历所有的日期格式进行匹配
            return parse(datetime, DATE_PATTERN.patterns());
        }
        return null;
    }

    /**
     * 在格式列表中查找一个能匹配的
     * @param datetime 日期
     * @param patterns 格式
     * @return {@link Date}
     */
    private static Date parse(String datetime, Set<String> patterns) {
        SimpleDateFormat format = LOCAL_FORMAT.get();
        for (String pattern : patterns) {
            format.applyPattern(pattern);
            Date date = Ex.tryCatch(() -> format.parse(datetime)).ignoreException(ParseException.class).orElse(null);
            if ( date != null )
                return date;
        }
        return null;
    }

    /**
     * <b>将日期字符串中带Z的替换为UTC</b>
     * <pre>{@code
     *  SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
     *  System.out.println(sdf.parse("2022-11-19T12:41:56.645UTC"));//Sat Nov 19 20:41:56 CST 2022
     *  System.out.println(sdf.parse("2022-11-19T12:41:56.645Z"));  //java.text.ParseException
     * }</pre>
     * @apiNote 替换为SimpleDateFormat能识别的时区
     * @return 返回将结尾的Z替换为UTC的日期
     */
    private static String replaceZtoUTC(String date) {
        char last = date.charAt(date.length() - 1);
        boolean equals = Objects.equals('Z', Character.toUpperCase(last));
        return equals ? date.substring(0, date.length() - 1) + "UTC" : date;
    }

    /**
     * <b>全局注册日期解析器</b>
     * <pre>{@code
     *     public static void main(String[] args) {
     *         SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
     *         System.out.println(format.format(to("2022-12-12")));    //2022-12-12 00:00:00
     *         System.out.println(format.format(to("2022年12月12日"))); //2022-01-01 00:00:00  仅解析有效位到'年'之前的部分
     *         register("yyyy年MM月dd日");
     *         System.out.println(format.format(to("2022年12月12日"))); //2022-12-12 00:00:00
     *     }
     * }</pre>
     * @see #register(String)
     * @param pattern 日期格式
     */
    public static void register(String pattern) {
        DATE_PATTERN.register(pattern);
    }

    /**
     * 封装日期格式长度和日期格式之间的映射（用于快速匹配格式）
     */
    private static class DatePattern {

        /**
         * 长度与格式的匹配
         */
        private final Map<Integer, Set<String>> patterns = new ConcurrentHashMap<>();

        /**
         * 注册一种日期格式
         * @param pattern 格式
         */
        public void register(String pattern) {
            int length = pattern.replace("'", "").length();
            Set<String> patternSet = patterns.get(length);
            if ( patternSet == null )
                patternSet = Collections.newSetFromMap(new ConcurrentHashMap<>());
            patternSet.add(pattern);
            patterns.put(length, patternSet);
        }

        /**
         * 匹配查找对应长度的格式
         * @param length 长度
         * @return 格式集合、如果匹配不到对应的长度的格式则返回null
         */
        public Set<String> patterns(int length) {
            return this.patterns.getOrDefault(length, Collections.emptySet());
        }

        /**
         * @return 格式列表
         */
        public Set<String> patterns() {
            return patterns.values().stream()
                    .flatMap(Set::stream)
                    .sorted(Comparator.comparing(String::length).reversed())
                    .collect(Collectors.toCollection(LinkedHashSet::new));
        }

    }

}
